From 9879cc55f62537f304dd2cfdd1eca419313d9426 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 15 Oct 2014 16:18:43 -0700 Subject: [PATCH] Add a metadata section to the lockfile In terms of future compatibility, Cargo may wish to add more information to lockfiles in the future. For example, the minimum required Rust version may one day be encoded into a lockfile. One of the major goals of a lockfile is for it to rarely change, and when it does change the diffs should be minimal. In terms of adding more information to the lockfile in the future, this presents a problem for older Cargo clients: 1. Cargo 2.0 adds a minimum required version of rust under the metadata.rustc key of the lockfile. 2. Cargo 1.0 is then used to update the `foo` dependency in the lockfile. When regenerating the lockfile, Cargo 1.0 discards the metadata inserted by Cargo 2.0. In order to future-proof ourselves in allowing new metadata in the future, Cargo is growing support now for transporting an opaque payload of metadata from one version of a lockfile to a new versions. This should solve the problem presented above. This metadata section is designed to be safely ignored by older Cargo versions (may just have some surprising behavior), while still allowing newer versions of Cargo to process the data. For example Cargo 2.0 would gracefully fail on an out-of-date rustc, while Cargo 1.0 may just present an obscure error message. --- src/cargo/core/resolver/encode.rs | 17 ++++++--- src/cargo/core/resolver/mod.rs | 10 ++++-- src/cargo/ops/cargo_fetch.rs | 15 +++++--- src/cargo/ops/cargo_generate_lockfile.rs | 26 +++++++++----- tests/test_cargo_generate_lockfile.rs | 44 ++++++++++++++++++++++++ 5 files changed, 92 insertions(+), 20 deletions(-) diff --git a/src/cargo/core/resolver/encode.rs b/src/cargo/core/resolver/encode.rs index 2eab82fe3..ec684d872 100644 --- a/src/cargo/core/resolver/encode.rs +++ b/src/cargo/core/resolver/encode.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, TreeMap}; use regex::Regex; use serialize::{Encodable, Encoder, Decodable, Decoder}; @@ -11,9 +11,12 @@ use super::Resolve; #[deriving(Encodable, Decodable, Show)] pub struct EncodableResolve { package: Option>, - root: EncodableDependency + root: EncodableDependency, + metadata: Option, } +pub type Metadata = TreeMap; + impl EncodableResolve { pub fn to_resolve(&self, default: &SourceId) -> CargoResult { let mut g = Graph::new(); @@ -30,7 +33,12 @@ impl EncodableResolve { } let root = self.root.to_package_id(default); - Ok(Resolve { graph: g, root: try!(root), features: HashMap::new() }) + Ok(Resolve { + graph: g, + root: try!(root), + features: HashMap::new(), + metadata: self.metadata.clone(), + }) } } @@ -136,7 +144,8 @@ impl> Encodable for Resolve { EncodableResolve { package: Some(encodable), - root: encodable_resolve_node(&self.root, &self.root, &self.graph) + root: encodable_resolve_node(&self.root, &self.root, &self.graph), + metadata: self.metadata.clone(), }.encode(s) } } diff --git a/src/cargo/core/resolver/mod.rs b/src/cargo/core/resolver/mod.rs index 8909d1d82..5179fc91e 100644 --- a/src/cargo/core/resolver/mod.rs +++ b/src/cargo/core/resolver/mod.rs @@ -11,6 +11,7 @@ use util::profile; use util::graph::{Nodes, Edges}; pub use self::encode::{EncodableResolve, EncodableDependency, EncodablePackageId}; +pub use self::encode::Metadata; mod encode; @@ -23,7 +24,8 @@ mod encode; pub struct Resolve { graph: Graph, features: HashMap>, - root: PackageId + root: PackageId, + metadata: Option, } pub enum ResolveMethod<'a> { @@ -37,7 +39,11 @@ impl Resolve { fn new(root: PackageId) -> Resolve { let mut g = Graph::new(); g.add(root.clone(), []); - Resolve { graph: g, root: root, features: HashMap::new() } + Resolve { graph: g, root: root, features: HashMap::new(), metadata: None } + } + + pub fn copy_metadata(&mut self, other: &Resolve) { + self.metadata = other.metadata.clone(); } pub fn iter(&self) -> Nodes { diff --git a/src/cargo/ops/cargo_fetch.rs b/src/cargo/ops/cargo_fetch.rs index e8820df8b..3f04af295 100644 --- a/src/cargo/ops/cargo_fetch.rs +++ b/src/cargo/ops/cargo_fetch.rs @@ -32,14 +32,19 @@ pub fn resolve_and_fetch(registry: &mut PackageRegistry, package: &Package) let lockfile = package.get_manifest_path().dir_path().join("Cargo.lock"); let source_id = package.get_package_id().get_source_id(); - match try!(ops::load_lockfile(&lockfile, source_id)) { - Some(r) => try!(add_lockfile_sources(registry, package, &r)), + let previous_resolve = try!(ops::load_lockfile(&lockfile, source_id)); + match previous_resolve { + Some(ref r) => try!(add_lockfile_sources(registry, package, r)), None => try!(registry.add_sources(package.get_source_ids())), } - let resolved = try!(resolver::resolve(package.get_summary(), - resolver::ResolveEverything, - registry)); + let mut resolved = try!(resolver::resolve(package.get_summary(), + resolver::ResolveEverything, + registry)); + match previous_resolve { + Some(ref prev) => resolved.copy_metadata(prev), + None => {} + } try!(ops::write_resolve(package, &resolved)); Ok(resolved) } diff --git a/src/cargo/ops/cargo_generate_lockfile.rs b/src/cargo/ops/cargo_generate_lockfile.rs index 533caed5c..43be2d6e8 100644 --- a/src/cargo/ops/cargo_generate_lockfile.rs +++ b/src/cargo/ops/cargo_generate_lockfile.rs @@ -47,7 +47,7 @@ pub fn update_lockfile(manifest_path: &Path, let lockfile = package.get_root().join("Cargo.lock"); let source_id = package.get_package_id().get_source_id(); - let resolve = match try!(load_lockfile(&lockfile, source_id)) { + let previous_resolve = match try!(load_lockfile(&lockfile, source_id)) { Some(resolve) => resolve, None => return Err(human("A Cargo.lock must exist before it is updated")) }; @@ -64,10 +64,10 @@ pub fn update_lockfile(manifest_path: &Path, match opts.to_update { Some(name) => { let mut to_avoid = HashSet::new(); - let dep = try!(resolve.query(name)); + let dep = try!(previous_resolve.query(name)); if opts.aggressive { - let mut visited = HashSet::new(); - fill_with_deps(&resolve, dep, &mut to_avoid, &mut visited); + fill_with_deps(&previous_resolve, dep, &mut to_avoid, + &mut HashSet::new()); } else { to_avoid.insert(dep.get_source_id()); match opts.precise { @@ -78,7 +78,7 @@ pub fn update_lockfile(manifest_path: &Path, None => {} } } - sources.extend(resolve.iter() + sources.extend(previous_resolve.iter() .map(|p| p.get_source_id()) .filter(|s| !to_avoid.contains(s)) .map(|s| s.clone())); @@ -87,10 +87,10 @@ pub fn update_lockfile(manifest_path: &Path, } try!(registry.add_sources(sources)); - let resolve = try!(resolver::resolve(package.get_summary(), - resolver::ResolveEverything, - &mut registry)); - + let mut resolve = try!(resolver::resolve(package.get_summary(), + resolver::ResolveEverything, + &mut registry)); + resolve.copy_metadata(&previous_resolve); try!(write_resolve(&package, &resolve)); return Ok(()); @@ -149,6 +149,14 @@ pub fn write_resolve(pkg: &Package, resolve: &Resolve) -> CargoResult<()> { emit_package(dep, &mut out); } + match e.toml.find(&"metadata".to_string()) { + Some(metadata) => { + out.push_str("[metadata]\n"); + out.push_str(metadata.to_string().as_slice()); + } + None => {} + } + try!(File::create(&loc).write_str(out.as_slice())); Ok(()) } diff --git a/tests/test_cargo_generate_lockfile.rs b/tests/test_cargo_generate_lockfile.rs index 5164ea2bc..9dd5c19bd 100644 --- a/tests/test_cargo_generate_lockfile.rs +++ b/tests/test_cargo_generate_lockfile.rs @@ -94,3 +94,47 @@ test!(adding_and_removing_packages { let lock4 = File::open(&lockfile).read_to_string().assert(); assert_eq!(lock1, lock4); }) + +test!(preserve_metadata { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + authors = [] + version = "0.0.1" + "#) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", r#" + [package] + name = "bar" + authors = [] + version = "0.0.1" + "#) + .file("bar/src/lib.rs", ""); + + assert_that(p.cargo_process("generate-lockfile"), + execs().with_status(0)); + + let metadata = r#" +[metadata] +bar = "baz" +foo = "bar" +"#; + let lockfile = p.root().join("Cargo.lock"); + { + let lock = File::open(&lockfile).read_to_string().assert(); + File::create(&lockfile).write_str((lock + metadata).as_slice()).assert(); + } + + // Build and make sure the metadata is still there + assert_that(p.process(cargo_dir().join("cargo")).arg("build"), + execs().with_status(0)); + let lock = File::open(&lockfile).read_to_string().assert(); + assert!(lock.as_slice().contains(metadata.trim()), "{}", lock); + + // Update and make sure the metadata is still there + assert_that(p.process(cargo_dir().join("cargo")).arg("update"), + execs().with_status(0)); + let lock = File::open(&lockfile).read_to_string().assert(); + assert!(lock.as_slice().contains(metadata.trim()), "{}", lock); +}) -- 2.30.2